package com.zengqingfa.basic.reflect;

import com.zengqingfa.basic.entity.Student;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * @fileName: ReflectTest1
 * @author: zengqf3
 * @date: 2020-9-5 9:14
 * @description:
 */
public class ReflectTest1 {

    /**
     * 获取class对象
     *
     * @throws Exception
     */
    @Test
    public void testGetClass1() throws Exception {
        //方法1：
        Class clazz1 = Student.class;
        System.out.println(clazz1);
        //方法2：
        Class<?> clazz2 = Class.forName("com.zengqingfa.basic.entity.Student");
        System.out.println(clazz2);
        //方法3：
        Student student = new Student();
        Class clazz3 = student.getClass();
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2);//true
        System.out.println(clazz1 == clazz3);//true
    }

    /**
     * 获取class相关属性和方法
     */
    @Test
    public void testClass1() {
        Class clazz1 = Student.class;
        //com.zengqingfa.basic.entity.Student
        System.out.println(clazz1.getName());
        //[@org.springframework.stereotype.Component(value=)]
        System.out.println(Arrays.toString(clazz1.getAnnotations()));
        //sun.misc.Launcher$AppClassLoader@18b4aac2
        System.out.println(clazz1.getClassLoader());
        //class com.zengqingfa.basic.entity.People
        System.out.println(clazz1.getSuperclass());
        //[public com.zengqingfa.basic.entity.Student()]
        System.out.println(Arrays.toString(clazz1.getConstructors()));
        //[public java.lang.String com.zengqingfa.basic.entity.Student.name]
        System.out.println(Arrays.toString(clazz1.getFields()));
    }

    /**
     * 通过class创建对象
     *
     * @throws Exception
     */
    @Test
    public void testClass2() throws Exception {
        Class clazz1 = Student.class;
        //通过无参构造函数创建对象 Student{age=null, name='null'}
        Student student1 = (Student) clazz1.newInstance();
        System.out.println(student1);
        student1.setAge(20);
        student1.setName("zqf");
        // Student{age=20, name='zqf'}
        System.out.println(student1);
    }

    /**
     * 测试构造函数
     */
    @Test
    public void testConstructor1() {
        Class clazz1 = Student.class;
        //获取所有的构造函数
        Constructor[] constructors = clazz1.getDeclaredConstructors();
        Arrays.stream(constructors).forEach(constructor -> {
            //获取权限
            constructor.setAccessible(true);
            //获取异常 [class java.lang.IllegalArgumentException]
            System.out.println(Arrays.toString(constructor.getExceptionTypes()));
            //获取构造函数上权限值,权限修饰符
            int modifiers = constructor.getModifiers();
            System.out.println(modifiers);//2
            System.out.println(Modifier.toString(modifiers));//private
            //获取构造函数上的参数 [java.lang.Integer age]
            System.out.println(Arrays.toString(constructor.getParameters()));
            //获取构造函数上的参数类型 [class java.lang.Integer]
            System.out.println(Arrays.toString(constructor.getParameterTypes()));
            //获取构造函数上的参数个数 1
            System.out.println(constructor.getParameterCount());
        });
    }

    /**
     * 通过构造函数创建对象
     *
     * @throws Exception
     */
    @Test
    public void testConstructor2() throws Exception {
        Class clazz1 = Student.class;
        //通过构造函数上参数类型获取对应的构造函数  Declared表示声明的所有的构造函数
        Constructor constructor = clazz1.getDeclaredConstructor(Integer.class);
        constructor.setAccessible(true);
        Student student = (Student) constructor.newInstance(22);
        //Student{age=22, name='null'}
        System.out.println(student);
    }

    /**
     * 方法
     *
     * @throws Exception
     */
    @Test
    public void testMethod1() throws Exception {
        Class clazz1 = Student.class;
        Method methods1 = clazz1.getMethod("sayHello", String.class, Integer.class);
        //public java.lang.String com.zengqingfa.basic.entity.Student.sayHello(java.lang.String,java.lang.Integer)
        // throws java.lang.IllegalStateException
        System.out.println(methods1);
        //[class java.lang.IllegalStateException]
        System.out.println(Arrays.toString(methods1.getExceptionTypes()));
        //class java.lang.String
        System.out.println(methods1.getReturnType());
        //sayHello
        System.out.println(methods1.getName());
        //[class java.lang.String, class java.lang.Integer]
        System.out.println(Arrays.toString(methods1.getParameterTypes()));
        //2
        System.out.println(methods1.getParameterCount());
        //[java.lang.String name1, java.lang.Integer age1]
        System.out.println(Arrays.toString(methods1.getParameters()));
    }

    /**
     * 通过反射调用方法
     *
     * @throws Exception
     */
    @Test
    public void testMethod2() throws Exception {
        Class clazz1 = Student.class;
        Method methods1 = clazz1.getMethod("sayHello", String.class, Integer.class);
        Student student = (Student) clazz1.newInstance();
        //invoke(Object obj, Object... args) obj：对象，args:方法参数
        Object lucy = methods1.invoke(student, "lucy", 27);
        //i am 27and i am lucy
        //say hello()
        System.out.println(lucy.toString());
    }

    /**
     * 字段相关的方法
     *
     * @throws Exception
     */
    @Test
    public void testField1() throws Exception {
        Class clazz1 = Student.class;
        Field name = clazz1.getField("name");
        //返回这个变量的类型 class java.lang.String
        System.out.println(name.getType());
        //Field.getGenericType()如果当前属性有签名属性类型就返回，否则就返回 Field.getType()
        //class java.lang.String
        System.out.println(name.getGenericType());
        /**
         * 获取成员变量的修饰符
         * 成员变量可以被以下修饰符修饰：
         * 访问权限控制符：public, protected, private
         * 限制只能有一个实例的：static
         * 不允许修改的：final
         * 不会被序列化：transient
         * 线程共享数据的一致性：volatile
         * 注解
         */
        //public
        System.out.println(Modifier.toString(name.getModifiers()));
    }

    /**
     * 设置字段
     *
     * @throws Exception
     */
    @Test
    public void testField2() throws Exception {
        Student student = new Student(20, "zengqingfa");
        Class clazz = student.getClass();
        Field nameField = clazz.getField("name");
        //获取对象上age的值 返回指定对象上此 Field 表示的字段的值。
        String name = (String) nameField.get(student);
        //zengqingfa
        System.out.println(name);
        /**获取和修改成员变量的值
         *可以在运行时修改它的成员变量的值
         * void set(Object obj, Object value)
         * 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
         */
        System.out.println("修改前：" + student.toString());//修改前：Student{age=20, name='zengqingfa'}
        nameField.set(student, "laishuhui111");
        System.out.println("修改后：" + student.toString());//修改后：Student{age=20, name='laishuhui111'}
        /**
         * private类型的，在外部类是无法直接访问到这些成员属性的，
         * 想要获取和修改只能通过B类的getters和setters方法进行。
         * 使用反射我可以绕过这些限制，因为是私有类型的要使用 Field.setAccessible(true); 方法解除限制
         */
        Field[] declaredFields = clazz.getDeclaredFields();
        //[private java.lang.Integer com.zengqingfa.basic.entity.Student.age,
        // public java.lang.String com.zengqingfa.basic.entity.Student.name]
        System.out.println(Arrays.toString(declaredFields));
        Arrays.stream(declaredFields).forEach(field -> {
            field.setAccessible(true);//获取权限
            //获取student对象的字段field里面的值
            try {
                //字段：java.lang.Integer,值：20
                //字段：java.lang.String,值：laishuhui111
                System.out.println("字段：" + field.getType().getName() + ",值：" + field.get(student));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });



    }
    @Test
    public void test11(){
        String str="aaabbbccc";
        String s = str.replaceAll("aa", "AA");
        System.out.println(s);
    }

    /**
     * 反射获取父类的私有方法
     */
    @Test
    public void test12(){
        Class clazz=Son.class;
        Class superclass = clazz.getSuperclass();
        Method[] declaredMethods1 = superclass.getDeclaredMethods();
        Method[] declaredMethods = clazz.getMethods();
        System.out.println(declaredMethods);
    }
}
